package com.zh.netty.nio;


import com.alibaba.fastjson.JSON;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Set;

/**
 * 服务器端
 * 编写一个NIO入门案例，实现服务器端和客户端之间的数据简单通讯（非阻塞）
 */
public class NIOServer {
    public static void main(String[] args) throws Exception {

        // 创建ServerSockerChannel -> ServerSocker
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();

        // 得到一个Selector对象
        Selector selector = Selector.open();

        // 绑定一个端口6666，在服务起端监听
        serverSocketChannel.socket().bind(new InetSocketAddress(6666));

        // 设置为非阻塞
        serverSocketChannel.configureBlocking(false);

        // 把serverSocketChannel 注册到 Selector, 关心事件为 OP_ACCEPT 连接事件
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

        // 循环等待客户端连接
        while (true){
            // 这里等待1秒
            if (selector.select(1000) == 0){ // 没有事件发生
                System.out.println("服务器等待了1秒，无连接");
                continue;
            }else {
                // 如果返回大于0, 就获取到相关的selectionKey集合
                // 如果返回大于0, 表示已经获取到关注的事件
                // selector.selectedKeys() 返回关注事件的集合
                // 通过selectionKeys 可以反向获取通道
                Set<SelectionKey> selectionKeys = selector.selectedKeys();

                // 遍历Set<SelectionKey>
                selectionKeys.forEach(s -> {
                    // 根据key 对应的通道发生的事件做相应的处理
                    // 如果是 OP_ACCEPT：连接事件，有新的客户端连接
                    if(s.isAcceptable()){
                        try {
                            // 该客户端生成一个SockerChannel
                            SocketChannel socketChannel = serverSocketChannel.accept();

                            System.out.println("客户端连接成功，生成了一个socketChannel："+ JSON.toJSONString(socketChannel));
                            // 将 SocketChannel 设置为非阻塞，否则会抛出异常java.nio.channels.IllegalBlockingModeException
                            socketChannel.configureBlocking(false);

                            // 将当前的socketChannel 注册到Selector中,关注事件为OP_READ
                            // 同时给socketChannel 关联一个Buffer
                            socketChannel.register(selector,SelectionKey.OP_READ, ByteBuffer.allocate(1024));
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }else if (s.isReadable()){ // 发生OP_READ 读取事件
                        // 通过 SelectionKey 反向获取到对应的channel
                        SocketChannel channel = (SocketChannel) s.channel();

                        // 获取到该 Channel 关联的Buffer
                        ByteBuffer buffer = (ByteBuffer)s.attachment();

                        try {
                            // 将channel 通道的数据读到buffer
                            channel.read(buffer);
                            System.out.println("form 客户端："+new String(buffer.array()));
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }

                    // 手动从集合中移除当前的selectKey ,防止重复操作
                    selectionKeys.remove(s);
                });
            }


        }
    }
}
